package se.webbzon.boltzmann.minigame.sandbox;

import java.awt.Image;
import java.awt.Rectangle;

import se.webbzon.boltzmann.configuration.Controls;
import se.webbzon.boltzmann.hud.CursorLayout;
import se.webbzon.boltzmann.hud.HudCursor;
import se.webbzon.boltzmann.input.Button;
import se.webbzon.boltzmann.input.Input;
import se.webbzon.boltzmann.input.InputListener;
import se.webbzon.boltzmann.resource.Resources;
import se.webbzon.oschi01.resourceset.ResourceSet;
import se.webbzon.oschi01.worldarea.WorldArea;
import se.webbzon.oschi01.worldobject.WorldObject;

public class SandBoxCursor implements SandBoxItem {
	
	// The maximal and minimal radius of the cursor
	private static final double maxRadius = 24;
	private static final double minRadius = 8;
	private static final double maxSpeed = 12;
	
	// The cursor instance of this sand box item
	private final Instance cursor;
	
	// The input listener of this sand box item
	private final InputListener listener;
	
	// The current sand box of this sand box item
	private SandBox box;
	
	// The item which is dragged by the cursor
	private SandBoxItem item;
	
	// The velocity of this cursor
	private int xVelocity, yVelocity;
	
	/*============================================================
	Constructors
	============================================================*/
	
	public SandBoxCursor() {
		cursor = new Instance();
		listener = new InputListenerImpl();
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	/** Returns the item grabbed by this cursor. **/
	public SandBoxItem getItem() {
		return item;
	}

	/** Sets the item grabbed by this cursor. **/
	public void setItem(SandBoxItem item) {
		this.item = item;
	}
	
	/** Returns true if the cursor is visible. **/
	public boolean isVisible() {
		return cursor.getVisible();
	}
	
	/** Sets whatever the cursor is visible. **/
	public void setVisible(boolean visible) {
		cursor.setVisible(visible);
	}
	
	@Override public void onAttach(SandBox box) {
		cursor.setVisible(true);
		Controls.instance().getInput().addListener(listener);
		Rectangle rect = new Rectangle();
		box.getBoundary(rect);
		setPosition((int) rect.getCenterX(),(int) rect.getCenterY());
		box.getWorld().createInstance(cursor);
		this.box = box;
	}

	@Override public void onDetach(SandBox box) {
		cursor.setVisible(false);
		Controls.instance().getInput().removeListener(listener);
		this.box = null;
		box.getWorld().destroyInstance(cursor);
	}

	@Override public boolean draggable() {
		return false;
	}

	@Override public boolean droppable() {
		return false;
	}

	@Override public void setPosition(double x, double y) {
		cursor.setPosition((int) x, (int) y);
	}

	@Override public double getX() {
		return cursor.getX();
	}

	@Override public double getY() {
		return cursor.getY();
	}

	@Override public double getWidth() {
		return maxRadius * 2;
	}

	@Override public double getHeight() {
		return maxRadius * 2;
	}
	
	/*============================================================
	Private Methods
	============================================================*/
	
	/** Returns the horizontal velocity of this cursor. **/
	private int getXVelocity() {
		return xVelocity;
	}

	/** Returns the vertical velocity of this cursor. **/
	private int getYVelocity() {
		return yVelocity;
	}

	/** Sets the velocity of this cursor. **/
	private void setVelocity(int xVelocity, int yVelocity) {
		this.xVelocity = xVelocity;
		this.yVelocity = yVelocity;
	}
	
	/*============================================================
	Private Classes
	============================================================*/

	/** The cursor instance. **/
	private final class Instance extends HudCursor {

		public Instance() {
			super(WorldObject.onStep, 1, new SandBoxCursorLayout());
		}

		@Override public SandBoxCursorLayout getLayout() {
			return (SandBoxCursorLayout) super.getLayout();
		}

		@Override public void onStep(WorldArea world) {
			super.onStep(world);
			int xn = getX() + getXVelocity(), yn = getY() + getYVelocity(); 
			if (box.isWithinSandbox(xn, yn))
				setPosition(xn,yn);
			SandBoxCursorLayout layout = getLayout();
			if (layout.getRadius() < maxRadius)
				layout.setRadius(Math.min(maxRadius, layout.getRadius() + 1));
			SandBoxItem item = getItem();
			if (item != null)
				item.setPosition(getX(), getY());
		}
		
	}

	/** An implementation of an input listener. **/
	private final class InputListenerImpl implements InputListener {

		@Override public void primaryAxisChanged(Input input, double direction, double value) {
			int vx = (int) (maxSpeed * value * Math.cos(direction));
			int vy = (int) (- maxSpeed * value * Math.sin(direction));
			setVelocity(vx,vy);
		}

		@Override public void secondaryAxisChanged(Input input, double direction, double value) {};

		@Override public void buttonPressed(Input input, Button button) {
			if (button == Button.ACTION) {
				SandBoxItem item = getItem();
				if (item == null) {
					item = box.pick(getX(), getY());
					setItem(item);
				} else {
					SandBoxContainer container = box.drop(item);
					if (container != null | item.droppable())
						setItem(null);
				}
				SandBoxCursorLayout layout = cursor.getLayout();
				layout.setRadius(minRadius);
			}
		}

		@Override public void buttonReleased(Input input, Button button) {};

		@Override public void bindingAccepted(Input input, Button button) {};

	}

	/** The layout of the cursor. **/
	private static final class SandBoxCursorLayout implements CursorLayout {

		// The image set of the cursor
		private final ResourceSet<Image> cursor = Resources.getImageSet("cursor_12p");

		// The radius of the cursor
		private double radius = maxRadius; 

		public void setRadius(double radius) {
			this.radius = radius;
		}

		@Override public double getRadius() {
			return radius;
		}

		@Override public ResourceSet<Image> getCursorImageSet() {
			return cursor;
		}

	}

}
